let obj = {
    name: '珠峰培训'
}
const fn = function fn(x, y, ev) {
    console.log(this, x, y, ev)
    return x + y
}

/* 
Function.prototype.bind = function bind(context, ...params) {
    // this:fn  context:obj  params:[10,20]
    let self = this
    return function anonymous(ev) {
        // this:document.body  ev:事件对象
        // 目的：把fn执行，改变其this指向，并且传递一个个的实参
        params.push(ev)
        return self.call(context, ...params)
    }
} 
*/

Function.prototype.bind = function bind(context, ...params) {
    let self = this
    return function anonymous(...args) {
        return self.call(context, ...params.concat(args))
    }
}
document.body.onclick = fn.bind(obj, 10, 20)
/*
document.body.onclick = function anonymous(ev) { ... }
//当点击的时候，先把bind返回的匿名函数执行
*/

// 需求：点击BODY执行fn，让fn中的this指向obj，传递给fn函数10/20，包括事件对象也作为最后一个参数传递给fn
// document.body.onclick = fn //点BODY才会执行fn -> this:document.body  x:事件对象  y:undefined
// document.body.onclick = fn.call(obj, 10, 20) //还不等到点击就把fn执行了，虽然this和参数都改了，但是ev事件对象没有，它相当于先把fn执行，把执行的结果“30”赋值给点击事件...
/* document.body.onclick = function (ev) {
    // this:document.body  ev:事件对象
    // 在此函数执行的时候，再把我们真正需要执行的函数fn执行
    return fn.call(obj, 10, 20, ev)
} */

//------------------------
/*
Function.prototype.call = function call(context, ...params) {
    // this:fn「需要改变this指向的函数」
    // context:obj「需要改变的this指向」
    // params:[10,20] 「需要传递给fn函数的实参」
    context.AAA = this  // obj.AAA = fn
    let result = context.AAA(...params) // obj.AAA(10,20)
    delete context.AAA // delete obj.AAA
    return result
}
*/

/* Function.prototype.call = function call(context, ...params) {
    if (context == null) context = window  //如果context传递的是null/undefined，则让函数中的this指向window对象
    if (!/^(object|function)$/.test(typeof context)) context = Object(context)  //如果传递的context值是原始值类型，是无法为其设置成员的，此时我们需要把其转换为对象类型「非标准特殊对象」
    let result,
        key = Symbol('KEY') //保证给对象新增的成员不会和对象以往的成员有冲突
    context[key] = this
    result = context[key](...params)
    delete context[key] //临时设置的这个成员仅仅是为了把函数执行，可以改变其内部的this，用完后需要移除
    return result
} */

/*
 处理步骤：
   @1 fn这个函数，首先基于 __proto__，找到 Function.prototype.call 方法
   @2 把找到的 call 方法执行
   @3 而在 call 方法内部：把fn执行，把它里面的this改为obj，并且为其传递了10/20，接收了它的返回值并作为call函数执行的返回值
 */
/* let res = fn.call(obj, 10, 20)
console.log(res) */

//------------------------
// 需求：把fn执行，传递10、20，并且让函数中的this指向obj
/* let res = fn.call(obj, 10, 20)
console.log(res)
res = fn.apply(obj, [10, 20])
console.log(res) */

/* 
// console.log(fn(10, 20)) //this:window  x:10  y:20  ->30
// console.log(obj.fn(10, 20)) //Uncaught TypeError: obj.fn is not a function  obj此时和fn没有任何的关系，obj.fn->undefined 
*/
/* 
obj.fn = fn
console.log(obj.fn(10, 20))  //手动让obj和fn有关系 
*/